/*
 * DoubleElement.java
 *
 * Created on July 26, 2007, 6:18 PM
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.wildcard;

import tal.drivechain.chain.ChainNode;
import tal.drivechain.wildcard.Wildcard.Operator;
import tal.drivechain.parsing.ParsingAid;
import tal.drivechain.expression.dcExpression;
import tal.drivechain.variable.types.DataType;
import tal.drivechain.midi.MidiUtility;

/**
 * A WildcardElement for using double precision numbers in a wildcard.
 * can work with every wildcard operator and supports expressions.
 * for example: [0..5.65] [{x-2},!{x*5}]
 * 
 * @author Tal Eisenberg
 */
public class DoubleElement implements WildcardElement {
    dcExpression elementExpression = null;
    
    public DoubleElement() { }
            
    public void parse(String element) {
        element = element.trim().toLowerCase();
        element = MidiUtility.parseNotes(element);
        if (ParsingAid.isNumber(element)) element = "${" + element + "}";
        else if (element.equals("true")) element = "${1}";
        else if (element.equals("false")) element = "${0}";
        elementExpression = new dcExpression(element, DataType.FLOAT);
    }
   
    public boolean validOperator(Operator opcode) {
        switch (opcode) {
            case AND:
            case ANY:
            case NOT:
            case ELEMENT:
            case OR:
            case RANGE:
                return true;
            default:
                return false;
        }
    }


    public WildcardElement makeWildcardElement(String element) {
        DoubleElement ret = new DoubleElement();
        ret.parse(element);
        return ret;
    }

    public Object getValue(ChainNode context) throws IllegalArgumentException {
        try {
            return (Double)elementExpression.evaluate(context);
        } catch (Exception e) {
            throw new IllegalArgumentException("Error while evaluating wildcard: " + e.getMessage());
        }
    }

    public boolean matches(Object value, ChainNode context) throws IllegalArgumentException {
        double num;
        try {
            num = (Double)value;
        } catch (ClassCastException ce) {
            try {
                num = (Long)value;
            } catch (ClassCastException cee) {
                try {
                    num = (Integer)value;
                } catch (ClassCastException ceee) {
                    throw new IllegalArgumentException("This wildcard can only accept numbers as it's elements (received " + value + ")");
                }
            }
        }
        double elementNum = (Double)getValue(context);
        return (num==elementNum);        
    }

    public boolean matchesElementFormat(String wc) {
        return ParsingAid.isNumber(wc);
    }

    public String toString(ChainNode context) {
        return this.getValue(context).toString();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof DoubleElement) {
            if (super.equals(obj)) return true;
            else {
                DoubleElement that = (DoubleElement)obj;
                return (this.elementExpression.equals(that.elementExpression));
            }
        } else return false;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 71 * hash + (this.elementExpression != null ? this.elementExpression.hashCode() : 0);
        return hash;
    }
}

